using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

namespace IQIGame.Onigao.GamePlay.Editors
{
    public static class ArtPrefabEditorMenu
    {
        public enum GenTargetType
        {
            None,
            Level,
            Battle,
            Both
        }

        private static List<string> paths;
        private static HashSet<string> _tempCheckAnimHashSet;

        [MenuItem("Assets/GameEditor/Prefab Tool/关卡->普通实体预制体", false, 1000)]
        static void GenPrefabLevelEntity()
        {
            paths = ConvertGUIDToPath(Selection.assetGUIDs);
            NextOne(0, ArtModeGenType.LevelEntity);
        }

        [MenuItem("Assets/GameEditor/Prefab Tool/关卡->主角团实体预制体", false, 1000)]
        static void GenPrefabLevelCharacter()
        {
            paths = ConvertGUIDToPath(Selection.assetGUIDs);
            NextOne(0, ArtModeGenType.LevelCharacter);
        }

        [MenuItem("Assets/GameEditor/Prefab Tool/战斗->英雄预制体", false, 1000)]
        static void GenPrefabBattleCharacter()
        {
            paths = ConvertGUIDToPath(Selection.assetGUIDs);
            NextOne(0, ArtModeGenType.BattleCharacter);
        }

        [MenuItem("Assets/GameEditor/Prefab Tool/战斗->怪物预制体", false, 1000)]
        static void GenPrefabBattleMonsterCharacter()
        {
            paths = ConvertGUIDToPath(Selection.assetGUIDs);
            NextOne(0, ArtModeGenType.BattleMonster);
        }

        [MenuItem("Assets/GameEditor/Prefab Tool/Live2D->预制体", false, 1000)]
        static void GenPrefabLive2D()
        {
            paths = ConvertGUIDToPath(Selection.assetGUIDs);
            NextOne(0, ArtModeGenType.Live2D);
        }
        
        [MenuItem("Assets/GameEditor/Prefab Tool/Spine->预制体", false, 1000)]
        static void GenPrefabSpine()
        {
            paths = ConvertGUIDToPath(Selection.assetGUIDs);
            NextOne(0, ArtModeGenType.Spine);
        }
        
        [MenuItem("Assets/GameEditor/Prefab Tool/Spine->UI预制体", false, 1000)]
        static void GenPrefabSpineUI()
        {
            paths = ConvertGUIDToPath(Selection.assetGUIDs);
            NextOne(0, ArtModeGenType.SpineUI);
        }

        #region 批量生成新增资源预设
        [MenuItem("GameEditor/实体资源生成/生成所有实体预设")]
        static void GenAllPrefab()
        {
            try
            {
                HashSet<string> prefabExistedNames = new();
                paths = new();

                #region 生成主角
                //主角一定会生成关卡和战斗的预设
                GetExistSceneEntityNames("/Res/02_Character", GenTargetType.Both, null, null, prefabExistedNames);
                CollectGenPaths("/Res/04_ArtModel/Character", prefabExistedNames, null, null, null, paths);
                //生成角色战斗预设
                NextOne(0, ArtModeGenType.BattleCharacter, true);
                //生成角色关卡预设
                NextOne(0, ArtModeGenType.LevelCharacter, true);
                #endregion 生成主角

                string[] battleExcludeDirNames = new string[] { "Common" };
                #region 生成怪物实体
                //monster_打头的为怪物，怪物生成关卡和战斗预设
                GetExistSceneEntityNames("/Res/03_Prefabs/Entity", GenTargetType.Both, "monster_", null, prefabExistedNames);
                CollectGenPaths("/Res/04_ArtModel/Entity", prefabExistedNames, battleExcludeDirNames, "monster_", null, paths);
                //生成实体战斗预设
                NextOne(0, ArtModeGenType.BattleMonster, true);
                //生成实体关卡预设
                NextOne(0, ArtModeGenType.LevelEntity, true);
                #endregion 生成怪物实体

                #region 生成普通实体
                //不以monster_打头的为普通npc，只生成关卡预设
                GetExistSceneEntityNames("/Res/03_Prefabs/Entity", GenTargetType.Level, null, "monster_", prefabExistedNames);
                CollectGenPaths("/Res/04_ArtModel/Entity", prefabExistedNames, battleExcludeDirNames, null, "monster_", paths);
                //生成实体关卡预设
                NextOne(0, ArtModeGenType.LevelEntity, true);
                #endregion 生成普通实体
            }
            catch (Exception e)
            {
                Debug.LogException(e);
            }

            EditorUtility.ClearProgressBar();
        }
        
        [MenuItem("GameEditor/实体资源生成/生成关卡实体预设")]
        static void GenLevelPrefab()
        {
            try
            {
                HashSet<string> prefabExistedNames = new();
                paths = new();

#region 生成主角
                //主角一定会生成关卡和战斗的预设
                GetExistSceneEntityNames("/Res/02_Character", GenTargetType.Both, null, null, prefabExistedNames);
                CollectGenPaths("/Res/04_ArtModel/Character", prefabExistedNames, null, null, null, paths);
                //生成角色关卡预设
                NextOne(0, ArtModeGenType.LevelCharacter, true);
#endregion 生成主角

                string[] battleExcludeDirNames = new string[] { "Common" };
#region 生成怪物实体
                //monster_打头的为怪物，怪物生成关卡和战斗预设
                GetExistSceneEntityNames("/Res/03_Prefabs/Entity", GenTargetType.Both, "monster_", null, prefabExistedNames);
                CollectGenPaths("/Res/04_ArtModel/Entity", prefabExistedNames, battleExcludeDirNames, "monster_", null, paths);
                //生成实体关卡预设
                NextOne(0, ArtModeGenType.LevelEntity, true);
#endregion 生成怪物实体

#region 生成普通实体
                //不以monster_打头的为普通npc，只生成关卡预设
                GetExistSceneEntityNames("/Res/03_Prefabs/Entity", GenTargetType.Level, null, "monster_", prefabExistedNames);
                CollectGenPaths("/Res/04_ArtModel/Entity", prefabExistedNames, battleExcludeDirNames, null, "monster_", paths);
                //生成实体关卡预设
                NextOne(0, ArtModeGenType.LevelEntity, true);
#endregion 生成普通实体
            }
            catch (Exception e)
            {
                Debug.LogException(e);
            }

            EditorUtility.ClearProgressBar();
        }
        
        [MenuItem("GameEditor/实体资源生成/生成战斗实体预设")]
        static void GenBattlePrefab()
        {
            try
            {
                HashSet<string> prefabExistedNames = new();
                paths = new();

#region 生成主角
                //主角一定会生成关卡和战斗的预设
                GetExistSceneEntityNames("/Res/02_Character", GenTargetType.Both, null, null, prefabExistedNames);
                CollectGenPaths("/Res/04_ArtModel/Character", prefabExistedNames, null, null, null, paths);
                //生成角色战斗预设
                NextOne(0, ArtModeGenType.BattleCharacter, true);
#endregion 生成主角

                string[] battleExcludeDirNames = new string[] { "Common" };
#region 生成怪物实体
                //monster_打头的为怪物，怪物生成关卡和战斗预设
                GetExistSceneEntityNames("/Res/03_Prefabs/Entity", GenTargetType.Both, "monster_", null, prefabExistedNames);
                CollectGenPaths("/Res/04_ArtModel/Entity", prefabExistedNames, battleExcludeDirNames, "monster_", null, paths);
                //生成实体战斗预设
                NextOne(0, ArtModeGenType.BattleMonster, true);
#endregion 生成怪物实体
            }
            catch (Exception e)
            {
                Debug.LogException(e);
            }

            EditorUtility.ClearProgressBar();
        }

        /// <summary>
        /// 收集需要批量生成的预设的路径
        /// </summary>
        /// <param name="resDirPath">Res打头的模型资源根目录，仅检查一级子目录</param>
        /// <param name="prefabExistedNames">已经生成过预设的实体名</param>
        /// <param name="excludeDirNames">排除的文件夹名，例如Common</param>
        /// <param name="includePrefix">以该前缀打头的实体资源名才生成，null表示无约束</param>
        /// <param name="excludePrefix">以该前缀打头的实体资源名不生成，null表示无约束</param>
        /// <param name="result">收集结果用的集合</param>
        private static void CollectGenPaths(string resDirPath, HashSet<string> prefabExistedNames, string[] excludeDirNames,
            string includePrefix, string excludePrefix, List<string> result)
        {
            result.Clear();
            string[] dirs = Directory.GetDirectories(Application.dataPath + resDirPath);
            foreach (var dir in dirs)
            {
                string entityName = Path.GetFileName(dir);
                string unityDir = dir.Substring(Directory.GetCurrentDirectory().Length + 1);
                //排除规则以外的
                if ((!string.IsNullOrEmpty(includePrefix) && !entityName.StartsWith(includePrefix))
                    || (!string.IsNullOrEmpty(excludePrefix) && entityName.StartsWith(excludePrefix)))
                {
                    continue;
                }
                //是否在排除列表里
                bool isNotInExcludeList = excludeDirNames == null || Array.IndexOf(excludeDirNames, entityName) == -1;

                //检查动画资源有没有变化（仅检查增删，修改并不需要重新生成）

                //关于参数animationPath写死成"Animation"，而不是从配置读取的解释：
                //1.找策划确认过，AnimationPath的配置已经形同虚设，目前已经用文件名前缀区分动作类型，而不是目录名，目录名都是Animation。
                //2.改动很大。需要提前初始化动画生成器，除非手动生成动画（另一个类），否则动画生成器的初始化目前是在预设生成器内部“运行时”完成的。
                //GenAllPrefab()里三种不同类型的生成代码就要变成6种，主城家园加入后可能会更多。
                //3.性能会变差，因为需要根据N种不同的逻辑来重复检查阶段，毕竟路径可能不同。
                //4.检查动画仅开发期会频繁生效，此时美术会频繁增删动作以测试不同情况，造成策划工作成本增加。
                //而正式情况下修改更多，增删极少，有也需要和策划确认。如果只考虑正式环境，策划是可以手动处理的。
                bool isAnimationChanged = CheckIsAnimationChanged(unityDir, "Animation");
                if (isNotInExcludeList && (!prefabExistedNames.Contains(entityName) || isAnimationChanged))
                {
                    result.Add(unityDir);
                }
            }
        }

        /// <summary>
        /// 获取当前已经生成过预设的实体资源名
        /// </summary>
        /// <param name="resDirPath">Res打头的预设资源根目录，仅检查一级子目录</param>
        /// <param name="genType">生成类型：关卡战斗都检查，只检查关卡预设，只检查战斗预设</param>
        /// <param name="includePrefix">以该前缀打头的实体资源名才检查，null表示无约束</param>
        /// <param name="excludePrefix">以该前缀打头的实体资源名不检查，null表示无约束</param>
        /// <param name="prefabExistedNames">收集已生成过的实体名的集合</param>
        static void GetExistSceneEntityNames(string resDirPath, GenTargetType genType, string includePrefix, string excludePrefix, HashSet<string> prefabExistedNames)
        {
            prefabExistedNames.Clear();
            string[] dirs = Directory.GetDirectories(Application.dataPath + resDirPath);
            foreach (string dir in dirs)
            {
                string entityName = Path.GetFileName(dir);
                if ((!string.IsNullOrEmpty(includePrefix) && !entityName.StartsWith(includePrefix))
                    || (!string.IsNullOrEmpty(excludePrefix) && entityName.StartsWith(excludePrefix)))
                {
                    continue;
                }
                string[] files = Directory.GetFiles(dir, "*.prefab", SearchOption.TopDirectoryOnly);
                if (genType == GenTargetType.Both) //两种预设（一个关卡一个战斗）都要生成的实体，只判断预设数量就行
                {
                    if (files.Length == 2) //预设都已生成
                    {
                        prefabExistedNames.Add(entityName);
                    }
                }
                else if (genType != GenTargetType.None) //只生成一种预设的实体
                {
                    foreach (var filePath in files)
                    {
                        string fileName = Path.GetFileNameWithoutExtension(filePath);
                        bool isRequiredPrefabExisted = (genType == GenTargetType.Level && fileName.StartsWith(ArtPrefabToolConstant.Prefab_Sign_Level))
                            || genType == GenTargetType.Battle && fileName.StartsWith(ArtPrefabToolConstant.Prefab_Sign_Battle);
                        if (isRequiredPrefabExisted) //预设都已生成
                        {
                            prefabExistedNames.Add(entityName);
                            break;
                        }
                    }
                }
            }
        }

        static List<string> ConvertGUIDToPath(string[] guids)
        {
            var paths = new List<string>(guids.Length);
            foreach (var guid in guids)
            {
                string path = AssetDatabase.GUIDToAssetPath(guid);
                if (ValidPath(path))
                {
                    paths.Add(path);
                }
            }
            return paths;
        }

        private static bool ValidPath(string path)
        {
            if (string.IsNullOrEmpty(path) || !Directory.Exists(path))
            {
                Debug.LogWarning("选择的不是文件夹:" + path);
                return false;
            }
            if (path.IndexOf("04_ArtModel") == -1 && path.IndexOf("17_Live2D") == -1)
            {
                Debug.LogWarning("必须在资源文件夹中选择资源生成");
                return false;
            }
            return true;
        }
        #endregion 批量生成新增资源预设

        /// <summary>
        /// 递归生成预设
        /// </summary>
        /// <param name="index"></param>
        /// <param name="genAnimatorType"></param>
        /// <param name="isIgnoreConfirm">是否忽略导出确认</param>
        private static void NextOne(int index, ArtModeGenType genAnimatorType, bool isIgnoreConfirm = false)
        {
            if (index >= paths.Count)
                return;
            GenPrefabByType(paths[index], genAnimatorType, isIgnoreConfirm);
            NextOne(index + 1, genAnimatorType, isIgnoreConfirm);
        }

        /// <summary>
        /// 导出预设
        /// </summary>
        /// <param name="path"></param>
        /// <param name="eGenAnimatorType"></param>
        /// <param name="isIgnoreConfirm"></param>
        public static void GenPrefabByType(string path, ArtModeGenType eGenAnimatorType, bool isIgnoreConfirm)
        {
            try
            {
                var entityName = Path.GetFileName(path);
                var generator = ArtPrefabToolConstant.ArtPrefabGenerator[(int)eGenAnimatorType];
                if (!generator.GenInitial(path, entityName))
                {
                    return;
                }
                generator.GenArtPrefab(true, isIgnoreConfirm);
            }
            catch (System.Exception e)
            {
                Debug.LogException(e);
            }
        }

        private static bool CheckIsAnimationChanged(string unityDir, string animationPath)
        {
            _tempCheckAnimHashSet ??= new();
            _tempCheckAnimHashSet.Clear();
            //获取当前的动画文件
            var animAssetPath = unityDir + "/" + animationPath;
            string[] guids = AssetDatabase.FindAssets("t:model", new string[] { animAssetPath });
            foreach (var guid in guids)
            {
                var p = AssetDatabase.GUIDToAssetPath(guid);
                var name = Path.GetFileNameWithoutExtension(p);
                _tempCheckAnimHashSet.Add(name);
            }
            //与生成纪录进行比较
            var record = ArtGenRecordUtil.GetRecord();
            //如果不存在纪录，在生成时判定为已变化（新增实体）
            if (!record.entityDir_AnimClips.TryGetValue(unityDir, out var recordClips))
            {
                return true;
            }
            //如果文件个数对不上，直接判定为已变化
            if (_tempCheckAnimHashSet.Count != recordClips.Count)
            {
                return true;
            }
            foreach (var curAnim in _tempCheckAnimHashSet)
            {
                if (!recordClips.Contains(curAnim)) //如果当前Clip不在纪录里，则判定为已变化（新增动画）,删除动画数量会变，前面已判断，修改动画内容不影响生成，判定为没变
                {
                    return true;
                }
            }
            return false;
        }
    }
}
